//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <elasys_server.h>
#include <wincetypes.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include "CBasicDirectory.h"
#include "CBasicFile.h"
#include "CDirStrEnumerator.h"

const wchar_t wszIllegal[] = L"\"*\\<>/?:|";

UInt32 CBasicDirectory::AddRef()
{
    LONG nRef = m_cRef.Increment();
    return (ULONG)nRef;
}

UInt32 CBasicDirectory::Release()
{
    LONG nRef = m_cRef.Decrement();

    if (0 == nRef) {
        delete this;
    }
    return nRef;
}

PInterface CBasicDirectory::Probe(REIID riid)
{
    if (EIID_IInterface == riid) {
        return (IInterface *)(IBasicDirectory *)this;
    }
    else if (EIID_IBasicFile == riid) {
        return (IBasicFile *)(IBasicDirectory *)this;
    }
    else if (EIID_IBasicDirectory == riid) {
        return (IBasicDirectory *)this;
    }
    else if (EIID_IFileMonitor == riid) {
        return (IFileMonitor *)this;
    }
    else if (EIID_CLASS_INFO == riid) {
        return (IInterface *)&ECLSID_CKtfsDirectory;
    }
    else if (EIID_INTERFACE_INFO == riid) {
        return (IInterface *)&EIID_IBasicDirectory;
    }

    return NULL;
}

CBasicDirectory::CBasicDirectory()
{
    m_wstrCurDir = NULL;
}

CBasicDirectory::CBasicDirectory(WString esDir)
{
    const wchar_t * dir = (const wchar_t *)esDir;
    if (dir[1] == L':') {
        m_wstrCurDir = (wchar_t *)malloc(sizeof(wchar_t) * (wcslen(L"\\")+1));
        assert(m_wstrCurDir != NULL);
        wcscpy(m_wstrCurDir, L"");
    }
    else {
        m_wstrCurDir = (wchar_t *)malloc(sizeof(wchar_t) * (wcslen(dir) + 1));
        assert(m_wstrCurDir != NULL);
        wcscpy(m_wstrCurDir, dir);
    }

    m_eFMode = FileAccessMode_ReadWrite;
}

CBasicDirectory::~CBasicDirectory()
{
    if (m_wstrCurDir) {
        free((void *)m_wstrCurDir);
    }
}

ECode CBasicDirectory::GetNameStat(
    wchar_t *wcsName, BOOL *bIsExist, BOOL *bIsDir)
{
    DWORD attr = GetFileAttributesW(wcsName);

    if (attr == 0xffffffff) {
        *bIsExist = FALSE;
        return S_FALSE;
    }
    else
        *bIsExist = TRUE;

    if (attr & FILE_ATTRIBUTE_DIRECTORY) {
        *bIsDir = TRUE;
    }
    else {
        *bIsDir = FALSE;
    }
    return NOERROR;
}

ECode CBasicDirectory::CombineName(
    WString name, wchar_t *fullname)
{
    assert(m_wstrCurDir);
    wchar_t *wcsName = fullname;
    wcscat(wcsName, m_wstrCurDir);
    wcscat(wcsName, L"\\");
    wcscat(wcsName, name);
    return NOERROR;
}

ECode CBasicDirectory::Aggregate(
    AggregateType type, PInterface pObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CBasicDirectory::GetDomain(
    IInterface **ppObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CBasicDirectory::Sync()
{
    // TODO: Add your code here
    return NOERROR;
}

ECode CBasicDirectory::GetType(
    /* [out] */ FileType * pType)
{
    if (!pType) {
        return E_INVALID_ARGUMENT;
    }

    *pType = FileType_Directory;
    return NOERROR;
}

ECode CBasicDirectory::GetAccessTime(
    /* [out] */ INT * pTime)
{
    if (!pTime) {
        return E_INVALID_ARGUMENT;
    }

    *pTime = m_iAccessTime;
    return NOERROR;
}

ECode CBasicDirectory::SetAccessTime(
    /* [in] */ INT time)
{
    m_iAccessTime = time;
    return NOERROR;
}

ECode CBasicDirectory::GetModificationTime(
    /* [out] */ INT * pTime)
{
    if (!pTime) {
        return E_INVALID_ARGUMENT;
    }

    *pTime = m_iModificationTime;
    return NOERROR;
}

ECode CBasicDirectory::SetModificationTime(
    /* [in] */ INT time)
{
    m_iModificationTime = time;
    return NOERROR;
}

ECode CBasicDirectory::GetCreateTime(
    /* [out] */ INT * pTime)
{
    if (!pTime) {
        return E_INVALID_ARGUMENT;
    }

    *pTime = m_iCreateTime;
    return NOERROR;
}

ECode CBasicDirectory::CheckAccessMode(
    /* [in] */ FileAccessMode mode)
{
    if (m_eFMode == mode) {
        return NOERROR;
    }
    else {
        return S_FALSE;
    }
}

ECode CBasicDirectory::GetAccessMode(
    /* [out] */ FileAccessMode * pMode)
{
    if (!pMode) {
        return E_INVALID_ARGUMENT;
    }

    *pMode = m_eFMode;
    return NOERROR;
}

ECode CBasicDirectory::SetAccessMode(
    /* [in] */ FileAccessMode mode)
{
    wchar_t *wcsName = (wchar_t *)m_wstrCurDir;
    DWORD attr = GetFileAttributesW(wcsName);
    if (mode == FileAccessMode_Read) {
        attr |= FILE_ATTRIBUTE_READONLY;
    }
    else {
        attr &= ~FILE_ATTRIBUTE_READONLY;
    }
    SetFileAttributesW(wcsName, attr);
    m_eFMode = mode;
    return NOERROR;
}

ECode CBasicDirectory::GetShareMode(
    /* [out] */ FileShareMode * pMode)
{
    return E_NOT_IMPLEMENTED;
}

ECode CBasicDirectory::SetShareMode(
    /* [in] */ FileShareMode mode)
{
    return E_NOT_IMPLEMENTED;
}

ECode CBasicDirectory::Lookup(
    /* [in] */ WString name,
    /* [out] */ IBasicFile ** piBasicFile)
{
    if (wcschr((const wchar_t *)name, '\\') ||
        wcschr((const wchar_t *)name, '/')) {
        return E_INVALID_ARGUMENT;
    }
    if (piBasicFile == NULL) return E_INVALID_ARGUMENT;

    {
        const wchar_t * pName = (const wchar_t *)name;
        if ((pName[0] == L'C') && (pName[1] == L':')) {

         CBasicDirectory *pBasicDirectory = new CBasicDirectory(name);
         if (pBasicDirectory == NULL) {
            return E_OUT_OF_MEMORY;
        }
        pBasicDirectory->AddRef();
        *piBasicFile = (IBasicFile *)pBasicDirectory;
        return NOERROR;
        }
    }
    BOOL bIsExist = FALSE, bIsDir = FALSE;
    wchar_t wcsName[_MAX_PATH] = {0};
    CombineName(name, wcsName);
    GetNameStat(wcsName, &bIsExist, &bIsDir);
    if (bIsExist && bIsDir) {
        CBasicDirectory *pBasicDirectory = new CBasicDirectory((WString)wcsName);
        if (pBasicDirectory == NULL) {
            return E_OUT_OF_MEMORY;
        }
        pBasicDirectory->AddRef();
        *piBasicFile = (IBasicFile *)pBasicDirectory;
        return NOERROR;
    }
    else if (!bIsExist) {
        //dir or file does not exist
        *piBasicFile = NULL;
        return E_PATH_NOT_FOUND;
    }
    else if (bIsExist && !bIsDir) {
        CBasicFile *pBasicFile = new CBasicFile((WString)wcsName, TRUE, FALSE);
        if (pBasicFile == NULL) {
            return E_OUT_OF_MEMORY;
        }
        pBasicFile->AddRef();
        *piBasicFile = (IBasicFile *)(IBasicDirectory *)pBasicFile;
        return NOERROR;
    }
    return E_INVALID_OPERATION;
}

ECode CBasicDirectory::CreateFile(
    /* [in] */ WString name,
    /* [in] */ FileAccessMode cmode,
    /* [in] */ Boolean bExclusive,
    /* [out] */ IBasicFile ** piBasicFile)
{
    CBasicFile *pBasicFile = NULL;
    if (name.IsNull()) {
        return E_INVALID_ARGUMENT;
    }

    if (wcspbrk((const wchar_t *)name, wszIllegal) != NULL) {
        return E_INVALID_ARGUMENT;
    }

    wchar_t wcsName[_MAX_PATH] = {0};
    CombineName(name, wcsName);

    DWORD attr = GetFileAttributesW(wcsName);
    if (attr == 0xffffffff) {  //file does not exist
        pBasicFile = new CBasicFile((WString)wcsName, FALSE, TRUE);
    }
    else if ((cmode & FileAccessMode_Write) ||
        (cmode & FileAccessMode_ReadWrite)) {
        //file exist and open with write access
        if (bExclusive == FALSE) {
            pBasicFile = new CBasicFile((WString)wcsName, TRUE, TRUE);
        }
        else {
            *piBasicFile = NULL;
            return E_ALREADY_EXIST; //TODO
        }
    }
    else {
        //file exist and open with read access
        if (bExclusive == FALSE) {
            pBasicFile = new CBasicFile((WString)wcsName, TRUE, FALSE);
        }
        else {
            *piBasicFile = NULL;
            return E_ALREADY_EXIST; //TODO
        }
    }
    if (!pBasicFile) return E_OUT_OF_MEMORY;

    pBasicFile->AddRef();
    pBasicFile->SetAccessMode(cmode);
    *piBasicFile = (IBasicFile *)pBasicFile;
    return NOERROR;
}

ECode CBasicDirectory::DeleteFile(
    /* [in] */ WString name)
{
    if (name.IsNull()) return E_INVALID_ARGUMENT;

    BOOL bIsExist = FALSE, bIsDir = FALSE;
    wchar_t wcsName[_MAX_PATH] = {0};
    CombineName(name, wcsName);
    GetNameStat(wcsName, &bIsExist, &bIsDir);
    if (!bIsExist) {
        //file doesnt exist
        return ECODE_FROM_ERRNO(ESRCH);
    }

    BOOL ret = ::DeleteFileW(wcsName);
    if (!ret) {
        wprintf(L"CBasicDirectory::DeleteFile: At file %d, line %d.\n", __FILE__, __LINE__);
        return E_INVALID_OPERATION;
    }
    return NOERROR;
}

ECode CBasicDirectory::Rename(
    /* [in] */ WString oldName,
    /* [in] */ IBasicDirectory * pNewDir,
    /* [in] */ WString newName)
{
    if (oldName.IsNull() || newName.IsNull()) {
        return E_INVALID_ARGUMENT;
    }

    BOOL bOldNameExist, bOldNameIsDir;
    BOOL bNewNameExist, bNewNameIsDir;
    wchar_t wcsOldName[_MAX_PATH] = {0};
    wchar_t wcsNewName[_MAX_PATH] = {0};

    CombineName(oldName, wcsOldName);
    ((CBasicDirectory *)pNewDir)->CombineName(newName, wcsNewName);

    GetNameStat(wcsOldName, &bOldNameExist, &bOldNameIsDir);
    GetNameStat(wcsNewName, &bNewNameExist, &bNewNameIsDir);
    if (!bOldNameExist) {
        //oldname doesnt exist
        return ECODE_FROM_ERRNO(ESRCH);
    }
    else if (bOldNameExist && bOldNameIsDir &&
        bNewNameExist && !bNewNameIsDir) {
        //oldname is dir and newname is file that has already exist
        return ECODE_FROM_ERRNO(ENOTDIR);
    }
    else if (bOldNameExist && !bOldNameIsDir &&
        bNewNameExist && bNewNameIsDir) {
        //oldname is file and newname is dir that has already exist
        return ECODE_FROM_ERRNO(EISDIR);
    }
    else if (bNewNameExist) {
        //newname exist
        return ECODE_FROM_ERRNO(EEXIST);
    }

    BOOL ret = MoveFileW(wcsOldName, wcsNewName);
    if (!ret) {
        wprintf(L"CBasicDirectory::Rename: At file %d, line %d.\n", __FILE__, __LINE__);
        return E_INVALID_OPERATION;
    }
    return NOERROR;
}

ECode CBasicDirectory::CreateDirectory(
    /* [in] */ WString name,
    /* [in] */ FileAccessMode mode)
{
    if (name.IsNull()) return E_INVALID_ARGUMENT;

    if (wcspbrk((const wchar_t *)name, wszIllegal) != NULL) {
        return ECODE_FROM_ERRNO(EINVAL);
    }

    BOOL bIsExist = FALSE, bIsDir = FALSE;
    wchar_t wcsName[_MAX_PATH] = {0};
    CombineName(name, wcsName);
    GetNameStat(wcsName, &bIsExist, &bIsDir);
    if (bIsExist && bIsDir) {
        return ECODE_FROM_ERRNO(EEXIST);
    }

    BOOL ret = CreateDirectoryW(wcsName, NULL);
    if (!ret) {
        wprintf(L"CBasicDirectory::CreateDirectory: At file %d, line %d.\n", __FILE__, __LINE__);
        return E_INVALID_OPERATION;
    }

    if (mode & FileAccessMode_Read) {
        DWORD attr = GetFileAttributesW(wcsName);
        attr |= FILE_ATTRIBUTE_READONLY;
        SetFileAttributesW(wcsName, attr);
    }
    return NOERROR;
}

ECode CBasicDirectory::DeleteDirectory(
    /* [in] */ WString name)
{
    if (name.IsNull()) return E_INVALID_ARGUMENT;

    BOOL bIsExist = FALSE, bIsDir = FALSE;
    wchar_t wcsName[_MAX_PATH] = {0};
    CombineName(name, wcsName);
    GetNameStat(wcsName, &bIsExist, &bIsDir);
    if (!bIsExist) {
        return ECODE_FROM_ERRNO(ESRCH);
    }

    BOOL ret = RemoveDirectoryW(wcsName);
    if (!ret) {
        wprintf(L"CBasicDirectory::DeleteDirectory: At file %d, line %d.\n", __FILE__, __LINE__);
        return E_INVALID_OPERATION;
    }
    return NOERROR;
}

ECode CBasicDirectory::GetDirEntries(
    /* [out] */ IWStringEnumerator ** piEntryNameEnumerator)
{
    if (!piEntryNameEnumerator) return E_INVALID_ARGUMENT;
    CDirStrEnumerator *pDirStrEnum = NULL;
    wchar_t wcsPath[_MAX_PATH] = {0};
    wcscat(wcsPath, (const wchar_t *)m_wstrCurDir);
    wcscat(wcsPath, L"\\*.*");
    pDirStrEnum = new CDirStrEnumerator(wcsPath);
    if (!pDirStrEnum) return E_OUT_OF_MEMORY;

    pDirStrEnum->AddRef();
    *piEntryNameEnumerator = pDirStrEnum;
    return NOERROR;
}

ECode CBasicDirectory::AddFileMonitor(
    /* [out] */ IEvent **pEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CBasicDirectory::RemoveFileMonitor(
    /* [in] */ IEvent *pEvent)
{
    return E_NOT_IMPLEMENTED;
}
